package top.frsf.rpc.server;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationContext;
import org.springframework.context.SmartLifecycle;
import top.frsf.rpc.annotations.RpcService;
import top.frsf.rpc.channel.ServerChannelInitializer;
import top.frsf.rpc.entity.ServiceInstance;
import top.frsf.rpc.registry.ServiceRegistry;

import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Map;

/**
 * @author He Kuntao
 * @since 2025/7/2 9:37
 */
@Slf4j
@RequiredArgsConstructor
public class NettyRpcServer implements SmartLifecycle {
    /**
     * Spring 容器
     */
    private final ApplicationContext context;
    /**
     * RPC 注册中心
     */
    private final ServiceRegistry registry;
    /**
     * 服务端口
     */
    private final int port;
    // ------------------------------------------------------------------------------------------------------------------
    /**
     * 服务是否运行
     */
    private volatile boolean running = false;
    /**
     * Netty boss线程组
     */
    private EventLoopGroup bossGroup;
    /**
     * Netty worker线程组
     */
    private EventLoopGroup workerGroup;

    @Override
    public void start() {
        Thread workerThread = new Thread(() -> {
            bossGroup = new NioEventLoopGroup(1);
            workerGroup = new NioEventLoopGroup();

            ServerBootstrap bootstrap = new ServerBootstrap();
            try {
                Channel serverChannel = bootstrap.group(bossGroup, workerGroup)
                        .channel(NioServerSocketChannel.class)
                        .childHandler(new ServerChannelInitializer(context))
                        .option(ChannelOption.SO_BACKLOG, 128)
                        .childOption(ChannelOption.SO_KEEPALIVE, true)
                        .bind(port)
                        .sync()
                        .channel();
                running = true;
                log.info("RPC 服务器启动成功，端口： {}", port);
                // 注册服务
                registryServices();

                // 阻塞等待服务停止
                serverChannel.closeFuture().sync();
            } catch (Exception e) {
                throw new RuntimeException("Netty server start failed", e);
            } finally {
                shutdown();
            }
        }, "netty-rpc-server");
        workerThread.start();
    }

    private void registryServices() throws UnknownHostException {
        // 获取所有@RpcService注解的bean
        Map<String, Object> serviceBeans = context.getBeansWithAnnotation(RpcService.class);
        if (serviceBeans.isEmpty()) {
            log.warn("未发现任何 @RpcService 标注的服务");
            return;
        }

        // 获取本地IP
        String ip = InetAddress.getLocalHost().getHostAddress();

        // 遍历注册Bean
        serviceBeans.forEach((beanName, bean) -> {
            Class<?>[] interfaces = bean.getClass().getInterfaces();
            if (interfaces.length == 0) {
                log.warn("服务 {} 未实现任何接口，无法注册", beanName);
                return;
            }

            // 以接口全限定名作为服务名进行注册
            for (Class<?> serviceInterface : interfaces) {
                String serviceName = serviceInterface.getName();
                ServiceInstance instance = new ServiceInstance(ip, port);
                registry.register(serviceName, instance);
                log.info("注册服务: {} -> {}", serviceName, instance);
            }
        });
    }

    @Override
    public void stop() {
        if (!isRunning()) {
            return;
        }

        running = false;
        shutdown();
    }


    @Override
    public boolean isRunning() {
        return running;
    }

    public void shutdown() {
        if (bossGroup != null) {
            bossGroup.shutdownGracefully();
        }
        if (workerGroup != null) {
            workerGroup.shutdownGracefully();
        }
    }
}
